/*
 * Copyright (c) 2022-2023 Termony Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef ASSEMBLY
#define ASSEMBLY 1
#endif

#include "los_arch_assembler.h"

FILE_BEGIN()

    EXPORT_LABEL(_vector_table)

    IMPORT_LABEL(g_exc)
    IMPORT_LABEL(_boot)
    IMPORT_LABEL(HalInterrupt)
    IMPORT_LABEL(HalExcHandleEntry)
    IMPORT_LABEL(HalTaskSwitch)
    IMPORT_LABEL(HalTaskSwitchFromISR)


    SECTION(.vectors)

FUNC_DEF(_vector_table)
    B HalResetExceptHdl         /* 复位异常. */
    B HalUndefInstrExceptHdl    /* 未定义异常. */
    B HalSvcExceptHdl           /* 系统调用异常. */
    B HalPrefetchAbortExceptHdl /* 指令预取终止异常. */
    B HalDataAbortExceptHdl     /* 数据访问终止异常. */
    NOP                         /* 预留, 未使用. */
    B HalIrqInterruptHdl        /* 外部中断或虚拟外部中断. */
    B HalFiqInterruptHdl        /* 快速中断或虚拟快速中断. */
    FUNC_END()


    SECTION(.text)

/* 保存异常上下文. */
MACRO_DEF(SAVE_EXC_CONTEXT)
    /* 保存SPSR和PC到管理模式栈中. */
    SRSDB   SP!, #SYS32_MODE
    /* 切换到管理模式. */
    CPS     #SYS32_MODE
    /* 保存LR到管理模式栈中. */
    STMFD   SP!, {LR}
    /* 保存进入异常时的SP到管理模式栈中. */
    MOV     LR, SP
    ADD     LR, LR, #(3 * 4)
    STMFD   SP!, {LR}
    /* 保存R0 ~ R12到管理模式栈中. */
    STMFD   SP!, {R0-R12}
MACRO_END()


/* 恢复异常上下文. */
MACRO_DEF(RESTORE_EXC_CONTEXT)
    /* 从管理模式栈中恢复R0 ~ R12. */
    LDMFD   SP!, {R0-R12}
    /* 跳过管理模式栈中保存的SP. */
    ADD     SP, SP, #(1 * 4)
    /* 从管理模式栈中恢复LR. */
    LDMFD   SP!, {LR}
    /* 从管理模式栈中恢复SPSR和PC. */
    RFEIA   SP!
MACRO_END()


FUNC_DEF(HalUndefInstrExceptHdl)
    /* 保存异常上下文. */
    SAVE_EXC_CONTEXT

    /* 1对应OS_EXCEPT_UNDEF_INSTR, 表示未定义指令异常. */
    MOV     R0, #1
    MOV     R1, SP
    /* 调用应用层异常处理程序. */
    BLX     HalExcHandleEntry

    /* 恢复异常上下文. */
    RESTORE_EXC_CONTEXT
    FUNC_END()


FUNC_DEF(HalPrefetchAbortExceptHdl)
    SUB     LR, LR, #4
    /* 保存异常上下文. */
    SAVE_EXC_CONTEXT

    /* 3对应OS_EXCEPT_PREFETCH_ABORT, 表示预取终止异常. */
    MOV     R0, #3
    MOV     R1, SP
    /* 获取指令故障状态寄存器IFSR. */
    MRC     P15, 0, R2, C5, C0, 1
    /* 获取指令故障地址寄存器IFAR. */
    MRC     P15, 0, R3, C6, C0, 2

    /* 调用应用层异常处理程序. */
    BLX     HalExcHandleEntry

    /* 恢复异常上下文. */
    RESTORE_EXC_CONTEXT
    FUNC_END()


FUNC_DEF(HalDataAbortExceptHdl)
    SUB     LR, LR, #8
    /* 保存异常上下文. */
    SAVE_EXC_CONTEXT

    /* 4对应OS_EXCEPT_DATA_ABORT, 表示数据终止异常. */
    MOV     R0, #4
    MOV     R1, SP
    /* 获取数据故障状态寄存器DFSR. */
    MRC     P15, 0, R2, C5, C0, 0
    /* 获取数据故障地址寄存器DFAR. */
    MRC     P15, 0, R3, C6, C0, 0

    /* 调用应用层异常处理程序. */
    BLX     HalExcHandleEntry

    /* 恢复异常上下文. */
    RESTORE_EXC_CONTEXT
    FUNC_END()


FUNC_DEF(HalResetExceptHdl)
    B       _boot
    FUNC_END()


FUNC_DEF(HalSvcExceptHdl)
    B       HalTaskSwitch
    FUNC_END()


FUNC_DEF(HalIrqInterruptHdl)
    SUB     LR, LR, #4
    /* 将寄存器R0 ~ R4, R12和LR保存到栈中. */
    PUSH    {R0-R4, R12, LR}
    /* 将SPSR保存到栈中. */
    MRS     LR, SPSR
    PUSH    {LR}

    /* 读取ICC_IAR的值到R0中. */
    MRC     P15, 0, R0, C12, C12, 0
    ISB
    /* 将ICC_IAR的值保存到栈中. */
    PUSH    {R0}

    /* 调用应用层中断处理程序. */
    BLX     HalInterrupt

    /* 从栈中恢复ICC_IAR的值到R0. */
    POP     {R0}
    /* 将R0写入ICC_EOIR来结束中断处理. */
    MCR     P15, 0, R0, C12, C12, 1
    ISB

    /* 从栈中恢复SPSR. */
    POP     {LR}
    MSR     SPSR_cxsf, LR
    /* 从栈中恢复寄存器R0 ~ R4, R12和LR. */
    POP     {R0-R4, R12, LR}

    /* 在中断中切换任务上下文. */
    B       HalTaskSwitchFromISR
    FUNC_END()


FUNC_DEF(HalFiqInterruptHdl)
    SUB     LR, LR, #4
    /* 将寄存器R0 ~ R4, R12和LR保存到栈中. */
    PUSH    {R0-R4, R12, LR}
    /* 将SPSR保存到栈中. */
    MRS     LR, SPSR
    PUSH    {LR}

    /* 读取ICC_IAR的值到R0中. */
    MRC     P15, 0, R0, C12, C12, 0
    ISB
    /* 将ICC_IAR的值保存到栈中. */
    PUSH    {R0}

    /* 调用应用层中断处理程序. */
    BLX     HalInterrupt

    /* 从栈中恢复ICC_IAR的值到R0. */
    POP     {R0}
    /* 将R0写入ICC_EOIR来结束中断处理. */
    MCR     P15, 0, R0, C12, C12, 1
    ISB

    /* 从栈中恢复SPSR. */
    POP     {LR}
    MSR     SPSR_cxsf, LR
    /* 从栈中恢复寄存器R0 ~ R4, R12和LR. */
    POP     {R0-R4, R12, LR}
    /* 跳转到中断返回地址(LR). */
    MOVS    PC, LR
    FUNC_END()

FILE_END()